//////////////////////////////////////////////////////////////////////////
///	\file:	SocketManager.cpp
///	Copyright (MingYou) 2009 - All Rights Reserved
///	Author:	Robert.Ma
///	Date:	[2009/05/19]
///	Description:
///		
//////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"


#include "SocketManager.h"
#include "INetCallback.h"
#include "SocketReader.h"
#include "SocketWriter.h"
#include "Define.h"
#include "InlineFunction.h"
#include "CETool.h"
#include "STLogFile.h"




#define MAX_TCP_PORT USHRT_MAX

BOOL	CSocketManager::m_bWSAStarted	= FALSE;

CSocketManager::CSocketManager(void)
		: m_Socket(INVALID_SOCKET),
		m_iRemotePort(-1),
		m_iErrorCode(0),
		m_socketState(NONE),
		m_pCallback(NULL),
		m_pNetRead(NULL),
		m_pNetWrite(NULL)
{
	ZeroMemory(m_szAddr, UCHAR_MAX);
}

BOOL CSocketManager::OpenNet()
{
	BOOL bResult = OpenWSA();

	STLOG_WRITE(L"Establish WSA is %d", bResult);

	return bResult;
}

BOOL CSocketManager::OpenWSA()
{
	BOOL bResult = TRUE;

	if ( !m_bWSAStarted )
	{
		WSADATA wsaData;
		int iErrorCode = ::WSAStartup(MAKEWORD(2, 2), &wsaData);

		bResult = m_bWSAStarted = (0 == iErrorCode);
	}

	return bResult;
}

void CSocketManager::CloseWSA()
{
	if ( m_bWSAStarted )
	{
		::WSACleanup();
		m_bWSAStarted = FALSE;
	}
}

void CSocketManager::CloseNet()
{
	CloseWSA();
}

CSocketManager::~CSocketManager(void)
{
	Release();
}

BOOL CSocketManager::Initialize(INetCallback* pCallback)
{
	BOOL bResult = FALSE;

	if ( NULL != pCallback )
	{
		m_pCallback = pCallback;

		bResult = TRUE;
	}

	return bResult;
}

void CSocketManager::Release()
{
	CMThread::Close();
}

void CSocketManager::DoDisConnect()
{
	SocketDisConnect();

	EndReadThread();
	EndWriteThread();
}

void CSocketManager::SocketDisConnect()
{
	if ( INVALID_SOCKET != m_Socket )
	{
		m_socketState = DISCONNECTING;

		::shutdown(m_Socket, SD_BOTH);
		::closesocket(m_Socket);

		m_Socket = INVALID_SOCKET;
		m_socketState = NONE;
	}
}

BOOL CSocketManager::SocketCreate()
{
	BOOL	dontLinger = TRUE;
	LINGER	lingerOpt;

	if ( !m_bWSAStarted )
	{
		m_iErrorCode = WSASYSNOTREADY;

		return FALSE;
	}

	//Exit if the socket has been already created
	if ( INVALID_SOCKET != m_Socket )
	{
		m_iErrorCode = WSAEACCES;

		return FALSE;
	}

	m_Socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	if ( INVALID_SOCKET == m_Socket )
	{
		m_iErrorCode = ::WSAGetLastError();

		return FALSE;
	}

	lingerOpt.l_onoff = 0;

	lingerOpt.l_linger = 0;

	if ( 0 != setsockopt(m_Socket, SOL_SOCKET, SO_DONTLINGER, (char*)&dontLinger, sizeof(BOOL)) )
	{
		m_iErrorCode = ::WSAGetLastError();
		::closesocket(m_Socket);

		return FALSE;
	}

	if ( 0 != setsockopt(m_Socket, SOL_SOCKET, SO_LINGER, (char*)&lingerOpt, sizeof(LINGER)) )
	{
		m_iErrorCode = ::WSAGetLastError();
		::closesocket(m_Socket);

		return FALSE;
	}

	//	OK, all done
	m_socketState = CREATED;

	return TRUE;
}

#define MULTI_TEST(bResult, n,func)\
for ( int i = 0; i < n; ++i ) \
{\
	bResult = func();\
	if ( bResult )\
	{\
		break;\
	}\
	else\
	{\
		::Sleep(500);\
	}\
}\

BOOL CSocketManager::DoConnect()
{
	BOOL bResult = FALSE;

	MULTI_TEST(bResult, 3, OpenNet);

	STLOG_WRITE(L"DoConnect, OpenNet: %d", bResult);

	if ( bResult )
	{
		bResult = SocketCreate();
	}

	STLOG_WRITE(L"DoConnect, SocketCreate: %d", bResult);

	if ( bResult )
	{
		bResult = SocketSetAddress();
	}

	STLOG_WRITE(L"DoConnect, SocketSetAddress: %d", bResult);

	if ( bResult )
	{
		MULTI_TEST(bResult, 3, SocketConnect);
	}

	STLOG_WRITE(L"DoConnect, SocketConnect: %d", bResult);

	if ( bResult )
	{
		OnConnected();
	}
	else
	{
		m_pCallback->OnError(m_iErrorCode);
	}

	return bResult;
}

BOOL CSocketManager::SocketConnect()
{
	STLOG_MARKER(L"CNetManager::SocketConnect");

	if ( !m_bWSAStarted )
	{
		m_iErrorCode = WSASYSNOTREADY;

		STLOG_WRITE(L"SocketConnect: WSASYSNOTREADY");

		return FALSE;
	}

	//	Make sure the socket was created
	if ( INVALID_SOCKET == m_Socket )
	{
		m_iErrorCode = WSAENOTSOCK;

		STLOG_WRITE(L"SocketConnect: WSAENOTSOCK");

		return FALSE;
	}

	//	We cannot connect if the socket is already connected.
	if ( CREATED < m_socketState )
	{
		m_iErrorCode = WSAEISCONN;

		STLOG_WRITE(L"SocketConnect: WSAEISCONN");

		return FALSE;
	}

	//	Connect.
	int iResult = ::connect(m_Socket, (SOCKADDR*)&m_remoteAddress, sizeof(SOCKADDR_IN));

	if ( 0 != iResult )
	{
		STLOG_WRITE(L"::connect() result: %d", iResult);

		m_iErrorCode = ::WSAGetLastError();

		STLOG_WRITE(L"SocketConnect: connect error, %d", m_iErrorCode);

		return FALSE;
	}

	m_socketState = CONNECTED;

	return TRUE;
}

BOOL CSocketManager::SocketSetAddress()
{
	ULONG		hostByIP	= 0;
	HOSTENT*	hostByName	= NULL;

	//	Builds destination address
	ZeroMemory(&m_remoteAddress, sizeof(SOCKADDR_IN));
	m_remoteAddress.sin_family = AF_INET;
	m_remoteAddress.sin_port = ::htons((USHORT)m_iRemotePort);

	hostByIP = ::inet_addr(m_szAddr);

	if ( INADDR_NONE == hostByIP )
	{
		hostByName = ::gethostbyname(m_szAddr);	//	Maybe cause system waiting...

		if ( hostByName == NULL )
		{
			m_iErrorCode = ::WSAGetLastError();
			return FALSE;
		}

		m_remoteAddress.sin_addr = * ((IN_ADDR*)hostByName->h_addr_list[0]);
	}
	else
	{
		m_remoteAddress.sin_addr.s_addr = hostByIP;
	}

	return TRUE;
}

BOOL CSocketManager::SetAddress(LPCSTR pszAddr, int iPort)
{
	//	Check port values
	if ( MAX_TCP_PORT < iPort )
	{
		m_iErrorCode = WSAEINVAL;

		return FALSE;
	}

	//	Gets address string and convert it from Unicode to multibyte
	int iHostLen = ::strlen(pszAddr);

	BOOL bResult = (UCHAR_MAX >= iHostLen);

	if ( bResult )
	{
		::memcpy_s(m_szAddr, UCHAR_MAX, pszAddr, iHostLen);
		m_iRemotePort = iPort;
	}

	return bResult;
}

BOOL CSocketManager::IsConnected()
{
	return (CONNECTED <= m_socketState);
}

void CSocketManager::OnConnected()
{
	StartReadThread();
	StartWriteThread();
	m_pCallback->OnConnected();
}

int CSocketManager::Write(const BYTE* pData, DWORD dwLen)
{
	int iResult = 0;

	if ( NULL != m_pNetWrite )
	{
		iResult =  m_pNetWrite->Write(pData, dwLen);
	}

	return iResult;
}

void CSocketManager::OnSend()
{
	if ( NULL != m_pCallback )
	{
		m_pCallback->OnSend();
	}
}

void CSocketManager::OnRecv()
{
	if ( NULL != m_pNetRead )
	{
		int iSize = m_pNetRead->GetBufferSize();
		if ( 0 < iSize )
		{
			BYTE* pData	= new BYTE[iSize];

			m_pNetRead->CopyBuffer(pData, iSize);
			m_pCallback->OnReceive(pData, (WORD)iSize);

			::ClearArrayPointer(pData);
		}
	}
}

void CSocketManager::OnClose(int /*iErrorID*/)
{}

BOOL CSocketManager::IsCacheFull()
{
	return (BUFFERSIZE >= m_pNetRead->GetBufferSize());
}

BOOL CSocketManager::Connect(const BYTE* pszAddr, int iPort)
{
	BOOL bResult = FALSE;

	if ( SetAddress((LPCSTR)pszAddr, iPort) && CMThread::Create() )
	{
		SafePostMessage(GetThreadID(), TM_NM_CONNECT);

		bResult =  TRUE;
	}

	return bResult;
}

void CSocketManager::DelayCheckConnection(DWORD dwDelay)
{
	::Sleep(dwDelay);
	SafePostMessage(GetThreadID(), TM_NM_CHECK_CONNECT);
}

DWORD CSocketManager::Execute()
{
	MSG msg;

	while ( ::GetMessage(&msg, NULL, 0, 0) )
	{
		switch ( msg.message )
		{
		case TM_NR_READ_RECV:
			OnRecv();
			break;

		case TM_SOCKET_ERROR:
			{
				if ( DISCONNECTING != m_socketState )
				{
					m_iErrorCode = (int)msg.wParam;
					if ( WSAEINTR == m_iErrorCode )
					{
						break;
					}

					if ( NULL != m_pCallback )
					{
						//	Deal error...
						m_pCallback->OnError(m_iErrorCode);
					}
				}
			}
			break;

		case TM_THREAD_EXIT:
			//	Do some clear...
			DoDisConnect();
			return 0;

		case TM_NM_CONNECT:
			DoConnect();
			break;

		case TM_NM_DISCONNECT:
			DoDisConnect();
			break;

		case TM_NM_CHECK_CONNECT:
			DelayCheckConnection(10000);
			break;

		default:
			break;
		}
	}

	return 0;
}

void CSocketManager::PreClose()
{
	SafePostMessage(GetThreadID(), TM_THREAD_EXIT);
}

void CSocketManager::AfterClose()
{}

void CSocketManager::PreCreate()
{}

void CSocketManager::AfterCreate()
{}

void CSocketManager::StartReadThread()
{
	if ( NULL == m_pNetRead )
	{
		m_pNetRead = new CSocketReader;
	}

	if ( NULL == m_pNetRead->GetThreadHandle() )
	{
		m_pNetRead->Initialize(&m_Socket, m_dwThreadID);
	}
}

void CSocketManager::StartWriteThread()
{
	if ( NULL == m_pNetWrite )
	{
		m_pNetWrite = new CSocketWriter;
	}

	if ( NULL == m_pNetWrite->GetThreadHandle() )
	{
		m_pNetWrite->Initialize(&m_Socket, m_dwThreadID);
	}
}

void CSocketManager::EndReadThread()
{
	::ClearPointer(m_pNetRead);
}

void CSocketManager::EndWriteThread()
{
	::ClearPointer(m_pNetWrite);
}
